﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using SYNANLib;
using System.Text.RegularExpressions;

namespace OffLineFilter
{
    public class Statistics
    {
        private String text;
        public String Text
        {
            get 
            {
                if (String.IsNullOrEmpty(text))
                {
                    using (StreamReader sr = new StreamReader(fileName, Encoding.Default))
                    {
                        text = sr.ReadToEnd();
                    }
                }
                return text; 
            }
        }

        private String fileName;
        public String FileName
        {
            get { return fileName; }
        }

        private Dictionary dictionary;
        public Dictionary Dictionary
        {
            get { return dictionary; }
        }

        private SyntaxHolder syntax;
        public SyntaxHolder Syntax
        {
            get
            {
                if (syntax == null)
                    syntax = new SyntaxHolder(resolveHomonyms: false);
                return syntax;
            }
        }

        public List<Sentence> Sentences
        {
            get { return Syntax.Sentences; }
        }

        #region Variances

        private double adverbVariance;
        public double AdverbVariance
        {
            get { return adverbVariance; }
        }

        private double interjectionVariance;
        public double InterjectionVariance
        {
            get { return interjectionVariance; }
        }

        private double particleVariance;
        public double ParticleVariance
        {
            get { return particleVariance; }
        }

        private double pretextVariance;
        public double PretextVariance
        {
            get { return pretextVariance; }
        }

        private double pronominalAdjectiveVariance;
        public double PronominalAdjectiveVariance
        {
            get { return pronominalAdjectiveVariance; }
        }

        private double pronominalNounVariance;
        public double PronominalNounVariance
        {
            get { return pronominalNounVariance; }
        }

        private double nounVariance;
        public double NounVariance
        {
            get { return nounVariance; }
        }

        private double verbVariance;
        public double VerbVarianve
        {
            get { return verbVariance; }
        }
        #endregion

        #region Percents

        private double adverbPercent;
        public double AdverbPercent
        {
            get { return adverbPercent; }
        }

        private double pronominalNounPercent;
        public double PronominalNounPercent
        {
            get { return pronominalNounPercent; }
            set { pronominalNounPercent = value; }
        }

        private double verbPercent;
        public double VerbPercent
        {
            get { return verbPercent; }
            set { verbPercent = value; }
        }

        private double pretextPercent;
        public double PretextPercent
        {
            get { return pretextPercent; }
            set { pretextPercent = value; }
        }

        private double nounPercent;
        public double NounPercent
        {
            get { return nounPercent; }
            set { nounPercent = value; }
        }

        private double intermediatePunctuationPercent;
        public double IntermediatePunctuationPercent
        {
            get { return intermediatePunctuationPercent; }
        }

        private double endPunctuationPercent;
        public double EndPunctuationPercent
        {
            get { return endPunctuationPercent; }
        }

        #endregion

        #region Lengths & Counts

        private long wordCount;
        private long punctuationCount;
        private long intermediatePunctuationCount;
        private long endPunctuationCount;
        private long adverbCount;
        private long pronominalNounCount;
        private long verbCount;
        private long pretextCount;
        private long nounCount;

        private double averageSentenceLength;
        public double AverageSentenceLength
        {
            get { return averageSentenceLength; }
        }

        private double averageNonstopSentenceLength;
        public double AverageNonstopSentenceLength
        {
            get { return averageNonstopSentenceLength; }
        }

        #endregion

        #region Dictionaries

        private Dictionary<String, double> variances;
        public Dictionary<String, double> Variances
        {
            get { return variances; }
        }

        private Dictionary<String, double> percents;
        public Dictionary<String, double> Percents
        {
            get { return percents; }
        }
        #endregion

        #region Predicates

        Predicate<Word> wordPredicate = word =>
           (!String.IsNullOrWhiteSpace(word.Pos) && !word.Lemma.IsPunctuation());

        Predicate<Word> punctuationPredicate = word =>
            (!String.IsNullOrWhiteSpace(word.Pos) && !word.Lemma.IsPunctuation());

        #endregion

        #region Data For Colors

        private List<List<ColorWord>> colorData = new List<List<ColorWord>>();
        public List<List<ColorWord>> ColorData
        {
            get { return colorData; }
        }
        
        #endregion

        public static String[] Poses = new[]
        {
            "С",
            "П",
            "МС",
            "Г",
            "ПРИЧАСТИЕ",
            "ДЕЕПРИЧАСТИЕ",
            "ИНФИНИТИВ",
            "МС-ПРЕДК",
            "МС-П",
            "ЧИСЛ",
            "ЧИСЛ-П",
            "Н",
            "ПРЕДК",
            "ПРЕДЛ",
            "СОЮЗ",
            "МЕЖД",
            "ЧАСТ",
            "ВВОДН",
            "КР_ПРИЛ",
            "КР_ПРИЧАСТИЕ", 
        };

        Regex sentencesRegex = new Regex(@"(?<![.?!]).*?[\s.?!]+(?=\s+)", RegexOptions.Singleline);
        IEnumerable<List<Word>> unitsBySentences;

        public void GetStatistics()
        {
            MatchCollection matches = sentencesRegex.Matches(Text);
            foreach (Match match in matches)
                Syntax.ProcessText(match.Value.Trim());

            GetVariancesAndLengths();
            GetCountsAndPercents();
            GetColorData();
        }

        public void GetVariancesAndLengths()
        {
            unitsBySentences = Syntax.Sentences.Select(sentence => sentence.Words);
            var wordsBySentences = unitsBySentences.Select(sentence => sentence.Where(word => wordPredicate(word)));
            var nonstopWordsBySentences = wordsBySentences.Select(sentence => sentence.Where(word =>
            {
                OffLineFilter.Dictionary.NumericPair pair = dictionary[word.Lemma.ToLower()];
                if (pair == null)
                    return true;
                return pair.IdfValue < IdfFilter.MinIdf;
            }));

            averageSentenceLength = wordsBySentences.Average(sentence => sentence.Count());
            averageNonstopSentenceLength = nonstopWordsBySentences.Average(sentence => sentence.Count());

            variances = wordsBySentences
                .Select(sentence => sentence.GroupBy(word => word.Pos))
                .Select(sentence => sentence.ToDictionary(group => group.Key, group => group.Count()))
                .SelectMany(counts => counts.Select(count => new { Pos = count.Key, PosCount = count.Value }))
                .GroupBy(group => group.Pos, group => group.PosCount)
                .Select(group => new { Pos = group.Key, Variance = group.Concat(Enumerable.Repeat(0, Syntax.Sentences.Count - group.Count())).Variance() })
                .ToDictionary(group => group.Pos, group => group.Variance);

            adverbVariance = variances.GetValueByKey("Н");
            interjectionVariance = variances.GetValueByKey("МЕЖД");
            particleVariance = variances.GetValueByKey("ЧАСТ");
            pretextVariance = variances.GetValueByKey("ПРЕДЛ");
            pronominalAdjectiveVariance = variances.GetValueByKey("МС-П");
            pronominalNounVariance = variances.GetValueByKey("МС");
            nounVariance = variances.GetValueByKey("С");
            verbVariance = variances.GetValueByKey("Г");
        }

        public void GetCountsAndPercents()
        {
            var units = Syntax.Sentences.SelectMany(sentence => sentence.Words);
            var punctuations = units.Where(word => punctuationPredicate(word));
            var words = units.Where(word => wordPredicate(word));
            wordCount = words.Count();

            var wordsByPoses = words.GroupBy(word => word.Pos).ToDictionary(group => group.Key, group => group.Count());
            percents = wordsByPoses.ToDictionary(pair => pair.Key, pair => (double)pair.Value / (double)wordCount * 100);
            var unknownWords = from word in words
                               where String.IsNullOrWhiteSpace(word.Pos)
                               select word.Lemma;

            // процент знаков в середине и конце предложения
            var punctuationsBySentences = unitsBySentences.Select(sentence => sentence.Where(word => punctuationPredicate(word)));
            intermediatePunctuationCount = punctuationsBySentences.Sum(puncts => puncts.Count() - 1);
            punctuationCount = punctuations.Count();
            endPunctuationCount = punctuationCount - intermediatePunctuationCount;
            intermediatePunctuationPercent = (double)intermediatePunctuationCount / (double)punctuationCount;
            endPunctuationPercent = (double)endPunctuationCount / (double)punctuationCount;
            percents.Add("ЗП-С", intermediatePunctuationPercent);
            percents.Add("ЗП-К", endPunctuationPercent);

            adverbCount = wordsByPoses.GetValueByKey("Н");
            pronominalNounCount = wordsByPoses.GetValueByKey("МС");
            verbCount = wordsByPoses.GetValueByKey("Г");
            pretextCount = wordsByPoses.GetValueByKey("ПРЕДЛ");
            nounCount = wordsByPoses.GetValueByKey("С");

            adverbPercent = percents.GetValueByKey("Н");
            pronominalNounPercent = percents.GetValueByKey("МС");
            verbPercent = percents.GetValueByKey("Г");
            pretextPercent = percents.GetValueByKey("ПРЕДЛ");
            nounPercent = percents.GetValueByKey("С");
        }

        public void GetColorData()
        {
            foreach (Sentence sentence in Syntax.Sentences)
            {
                List<ColorWord> words = new List<ColorWord>();
                foreach (Word word in sentence.Words)
                {
                    String commonGrammems = word.CommonGrammems;
                    String gramDescriptions = word.GramDescription.Split(' ', ';').FirstOrDefault();
                    if (gramDescriptions == null)
                        gramDescriptions = "";
                    words.Add(new ColorWord() { ColorKey = word.Pos + commonGrammems + "|" + gramDescriptions });
                }
                colorData.Add(words);
            }
        }

        public Statistics(String fileName, Dictionary dictionary)
        {
            if (String.IsNullOrEmpty(fileName))
                throw new ArgumentNullException();
            this.fileName = fileName;
            this.dictionary = dictionary;
        }
    }


}
